from enum import IntEnum
from typing import (TYPE_CHECKING, Any, Callable, Concatenate, Final, Literal,
                    Optional, ParamSpec, cast, overload)

from .utilities import Sequence

if TYPE_CHECKING:
    from . import Wealth

P = ParamSpec('P')

class Property:
    class Status(IntEnum):
        NORMAL = 0
        MORTGAGED = 1
        SOLD = 2
    
    class GetterType(IntEnum):
        Pricer = 0
        Valuer = 1
    
    @overload
    def __init__(self,
                 pricer: Callable[Concatenate[GetterType, dict[str, Any], P], int],
                 *args: P.args, **kwargs: P.kwargs) -> None: ...
    
    @overload
    def __init__(self,
                 pricer: Callable[Concatenate[Literal[GetterType.Pricer], dict[str, Any], P], int],
                 valuer: Callable[Concatenate[Literal[GetterType.Valuer], dict[str, Any], P], int],
                 *args: P.args, **kwargs: P.kwargs) -> None: ...

    def __init__(self,
                 pricer: Callable[Concatenate[Literal[GetterType.Pricer], dict[str, Any], P], int],
                 valuer: Callable[Concatenate[Literal[GetterType.Valuer], dict[str, Any], P], int] | None = None,
                 *args: P.args, **kwargs: P.kwargs) -> None:
        self._data: dict[str, Any] = {}
        self._owner_id: int = -1
        self._pricer: Final = pricer
        self._valuer: Final = valuer if not valuer is None else cast(Callable[Concatenate[Literal[Property.GetterType.Valuer], dict[str, Any], P], int], pricer)
        self._getter_args: Final = args
        self._getter_kwargs: Final = kwargs
        self._status: Property.Status = Property.Status.NORMAL
        
    def mortgagable(self) -> bool:
        return self._status == Property.Status.NORMAL
    
    def mortgage(self) -> None:
        self._status = Property.Status.MORTGAGED
    
    def redeem(self) -> None:
        self._status = Property.Status.NORMAL
    
    def sell(self) -> None:
        self._status = Property.Status.SOLD
        self._owner_id = -1
    
    def removable(self) -> bool:
        return self._status == Property.Status.SOLD

    def onremove(self) -> None:
        self._status = Property.Status.NORMAL
        self._owner_id = -1

    @property
    def price(self) -> int:
        return self._pricer(Property.GetterType.Pricer, self._data, *self._getter_args, **self._getter_kwargs)
    
    @property
    def value(self) -> int:
        return self._valuer(Property.GetterType.Valuer, self._data, *self._getter_args, **self._getter_kwargs)
    
    def ownby(self, owner: int) -> bool:
        return self._owner_id == owner
    
    def copy(self) -> 'Property':
        p = Property(self._pricer, self._valuer, *self._getter_args, **self._getter_kwargs)
        p._data = self._data.copy()
        return p
    
    def __repr__(self) -> str:
        return object.__repr__(self) \
                .replace("object", f"object [owner={self._owner_id}, value={self.value}, status={self._status.name}, data={self._data}]")

class PropertiesManager:
    def __init__(self, *, origin: 'Optional[PropertiesManager]' = None) -> None:
        self._properties: Sequence[Property] = Sequence(origin=origin._properties if not origin is None else None)

    def register(self, p: Property) -> int:
        return self._properties.push(p)
    
    def buy(self, wealth: 'Wealth', id: int) -> bool:
        if not self._properties.exist(id):
            return False
        else:
            p = self._properties.get(id)
        
        price = p.price
        if wealth.account.balance <= price:
            return False
        else:
            wealth.account.decrease(price, "Buy")
            wealth.properties.push(p)
            p._owner_id = wealth.id
            return True